package com.github.caiqy.shirodemo.config;

import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 使用redis管理session
 *
 * @author caiqy
 * @date 2019 08 26
 */
@Slf4j
@Component("sessionDao")
public class RedisSessionDao extends AbstractSessionDAO {

    private static final String PREFIX_KEY = "{talent-sess}:";
    private static final String REDIS_SESSION_SET_NAME = "{talent-sess-set}:";

    private ValueOperations<String, Object> vo;

    private SetOperations<String, Object> so;

    private String getKey(String key) {
        return (PREFIX_KEY + key);
    }

    @Autowired
    public RedisSessionDao(ValueOperations<String, Object> vo, SetOperations<String, Object> so) {
        super();
        this.vo = vo;
        this.so = so;
    }

    private void saveSession(Session session) {
        if (session != null && session.getId() != null) {
            String key = getKey(session.getId().toString());
            vo.set(key, session, 2, TimeUnit.DAYS);
            // 创一个保存所有sessionId的集合
            so.add(REDIS_SESSION_SET_NAME, key);
        }
    }

    @Override
    protected Serializable doCreate(Session session) {
        Serializable sessionId = generateSessionId(session);
        assignSessionId(session, sessionId);
        saveSession(session);
        return sessionId;
    }

    @Override
    protected Session doReadSession(Serializable sessionId) {
        if (log.isInfoEnabled()) {
            log.trace("Read redis session.");
        }
        if (sessionId == null) {
            return null;
        }
        String key = getKey(sessionId.toString());
        Object value = vo.get(key);
        if (value == null) {
            return null;
        }
        return (SimpleSession) value;
    }

    @Override
    public void update(Session session) throws UnknownSessionException {
        saveSession(session);
    }

    @Override
    public void delete(Session session) {
        if (session == null || session.getId() == null) {
            return;
        }
        String key = getKey(session.getId().toString());
        vo.getOperations().delete(key);
        so.remove(REDIS_SESSION_SET_NAME, session.getId().toString());
    }

    @Override
    public Collection<Session> getActiveSessions() {
        Set<Object> set = so.members(REDIS_SESSION_SET_NAME);
        Set<Session> sessions = new HashSet<>();
        if (!CollectionUtils.isEmpty(set)) {
            for (Object s : set) {
                Session session = doReadSession(s.toString());
                if (session != null) {
                    sessions.add(session);
                }
            }
        }
        return sessions;
    }
}
